#include <stdint.h>

#include "lvgl.h"
#include "lv_examples.h"

#include "co_printf.h"
#include "os_timer.h"

#include "driver_system.h"
#include "driver_timer.h"
#include "driver_display.h"
#include "driver_touchpad.h"

#define LV_TICK_COUNT               10

static os_timer_t lv_schedule_timer;

static lv_disp_buf_t disp_buf;

static lv_disp_drv_t *last_disp = NULL;

// this function is called in DMA interrupt
static void my_disp_flush_done(void)
{
//    lv_disp_flush_ready(last_disp);
    last_disp = NULL;
}

static void my_disp_flush(lv_disp_drv_t * disp, const lv_area_t * area, lv_color_t * color_p)
{
    if(last_disp != NULL) {
        display_wait_transfer_done();
    }
    last_disp = disp;
    display_set_window(area->x1, area->x2, area->y1, area->y2);
    display_update((area->x2+1-area->x1)*(area->y2+1-area->y1), (void *)color_p, my_disp_flush_done);
    lv_disp_flush_ready(last_disp);
}

static _Bool my_touchpad_read(struct _lv_indev_drv_t * indev, lv_indev_data_t * data)
{
    uint8_t buffer[8];
    
    touchpad_read_data_raw(buffer, 8);
    data->state = (buffer[2] != 0) ? LV_INDEV_STATE_PR : LV_INDEV_STATE_REL;
    if(data->state == LV_INDEV_STATE_PR) {
        data->point.x = ((buffer[3]&0x0f)<<8) | buffer[4];
        data->point.y = ((buffer[5]&0x0f)<<8) | buffer[6];
    }

    return false; /*Return `false` because we are not buffering and no more data to read*/
}

struct_Timer_t Timer0_Handle;

__attribute__((section("ram_code"))) void timer0_isr(void)
{
    timer_int_clear(Timer0);

    lv_tick_inc(LV_TICK_COUNT);
    uart_putc_noint_no_wait(UART0_BASE, 'T');
}

__attribute__((section("ram_code"))) void timer1_isr(void)
{
    uint32_t tick;
    timer_int_clear(Timer1);
    timer_stop(Timer1);

    tick = lv_task_handler();
    timer_init(Timer1, system_get_clock_config()*1000*tick, TIMER_DIV_NONE);
    timer_start(Timer1);
}

static void lv_schedule_timer_handler(void *arg)
{
    lv_task_handler();
}

void gui_main(void)
{
    touchpad_init(NULL);
    display_init();
    
    lv_init();

//    static uint16_t buf[LV_BUFFER_SIZE];
//    lv_disp_buf_init(&disp_buf, buf, NULL, LV_BUFFER_SIZE);    /*Initialize the display buffer*/
    lv_disp_buf_init(&disp_buf, (void *)0x22000000, (void *)0x22050000/*(0x22000000+LV_HOR_RES_MAX * LV_VER_RES_MAX * sizeof(lv_color_t))*/, LV_HOR_RES_MAX * LV_VER_RES_MAX);    /*Initialize the display buffer*/

    /* Implement and register a function which can copy the rendered image to an area of your display */
    lv_disp_drv_t disp_drv;               /*Descriptor of a display driver*/
    lv_disp_drv_init(&disp_drv);          /*Basic initialization*/
    disp_drv.flush_cb = my_disp_flush;    /*Set your driver function*/
    disp_drv.buffer = &disp_buf;          /*Assign the buffer to the display*/
    lv_disp_drv_register(&disp_drv);      /*Finally register the driver*/

    /* Implement and register a function which can read an input device. E.g. for a touch pad */
    lv_indev_drv_t indev_drv;                  /*Descriptor of a input device driver*/
    lv_indev_drv_init(&indev_drv);             /*Basic initialization*/
    indev_drv.type = LV_INDEV_TYPE_POINTER;    /*Touch pad is a pointer-like device*/
    indev_drv.read_cb = my_touchpad_read;      /*Set your driver function*/
    lv_indev_drv_register(&indev_drv);         /*Finally register the driver*/

    __SYSTEM_TIMER_CLK_ENABLE();
    timer_init(Timer0, system_get_clock_config()*1000*LV_TICK_COUNT, TIMER_DIV_NONE);
    timer_start(Timer0);
    NVIC_SetPriority(TIMER0_IRQn, 5);
    NVIC_EnableIRQ(TIMER0_IRQn);

//    os_timer_init(&lv_schedule_timer, lv_schedule_timer_handler, NULL);
//    os_timer_start(&lv_schedule_timer, 20, true);
    timer_init(Timer1, system_get_clock_config()*1000*100, TIMER_DIV_NONE);
    timer_start(Timer1);
    NVIC_SetPriority(TIMER1_IRQn, 6);
    NVIC_EnableIRQ(TIMER1_IRQn);

    //lv_demo_widgets();
    lv_demo_benchmark();
    //lv_ex_calendar_1();
    //lv_demo_music();
}

